import { AssetManager,Asset,__private,assetManager,resources,js,error,warn } from "cc";
import { hiyooo } from "../../HiYOOO";
// import { warn } from "console"; // 这里报错了。如果从console 导入，这里则会报错
 

export type Paths  =  string | string[] 
// export type AssetType<T extends Asset> = __private.__types_globals__Constructor<T> | null;
export type AssetType<T = Asset> = __private.__types_globals__Constructor<T> | null;
export type ProgressCallback = (finished: number, total: number, item: AssetManager.RequestItem) => void | null;
export type CompleteCallback = any ;

 

interface ILoadResArgs<T extends Asset> {
    /** 资源包名 */
    bundle?: string;
    /** 资源文件夹名 */
    dir?: string;
    /** 资源路径 */
    paths: Paths;
    /** 资源类型 */
    type: AssetType<T>;
    /** 资源加载进度 */
    onProgress: ProgressCallback;
    /** 资源加载完成 */
    onComplete: CompleteCallback;
    /** 是否为预加载 */
    preload?: boolean;
}

export class ResLoader {
    //默认资源加载路径，可以修改，防止不传的时候，值为null
    defaultBundleName:string = "resources";

    //是否使用cdn资源
    cdn:boolean = false;

      /** 下载时的最大并发数 - 项目设置 -> 项目数据 -> 资源下载并发数，设置默认值；初始值为15 */
    get maxConcurrency(){
        return assetManager.downloader.maxConcurrency;
    }
    set maxConcurrency(value:number){
        assetManager.downloader.maxConcurrency = value;
    }

    //下载时，每帧最大请求数
    get maxRequestsPerFrame(){
        return assetManager.downloader.maxRequestsPerFrame;
    }
    set maxRequestsPerFrame(value:number){
        assetManager.downloader.maxRequestsPerFrame = value;
    }

    //下载失败时，最大重试次数
    get maxRetryCount(){
        return assetManager.downloader.maxRetryCount;
    }
    set maxRetryCount(value:number){
        assetManager.downloader.maxRetryCount = value;
    }

    // 重试的间隔时间，单位为毫秒。
    get RetryInterval(){
        return assetManager.downloader.retryInterval;
    }
    set RetryInterval(value:number){
        assetManager.downloader.retryInterval = value;
    }

    // 下载资源包配置信息
    private bundles:Map<string,string> = new Map<string,string>();

    init(config:any):void{
        this.cdn = config.enable;
        for(let bundleName in config.packages){
            this.bundles.set(bundleName,config.packages[bundleName]);
        }
    }

    // 加载资源
    public load<T extends Asset>(bundleName:string,path:Paths,type:AssetType<T>,onProgress:ProgressCallback,onComplete:CompleteCallback) : void;
    public load<T extends Asset>(bundleName:string,path:Paths,onProgress:ProgressCallback,onComplete:CompleteCallback) :void;
    public load<T extends Asset>(bundleName:string,path:Paths,onComplete:CompleteCallback):void;
    public load<T extends Asset>(bundleName:string,path:Paths,type:AssetType<T>,onComplete:CompleteCallback) :void;
    public load<T extends Asset>(path:Paths,type:AssetType<T>,onProgress:ProgressCallback,onComplete:CompleteCallback) :void;
    public load<T extends Asset>(path:Paths,onProgress:ProgressCallback,onComplete:CompleteCallback):void;
    public load<T extends Asset>(path:Paths,onComplete:CompleteCallback):void;
    public load<T extends Asset>(path:Paths,type:AssetType<T>,onComplete:CompleteCallback):void;

    public load<T extends Asset>(
        bundleName:string,
        path?:Paths | AssetType<T> | ProgressCallback | CompleteCallback,
        type?:AssetType<T> | ProgressCallback | CompleteCallback,
        onProgress?:ProgressCallback | CompleteCallback,
        onComplete?:CompleteCallback,
    ){

            let args: ILoadResArgs<T> | null = null;
            if (typeof path === "string" || path instanceof Array) {
                args = this.parseLoadResArgs(path, type, onProgress, onComplete);
                args.bundle = bundleName;
            }
            else {
                args = this.parseLoadResArgs(bundleName, path, type, onProgress);
                args.bundle = this.defaultBundleName;
            }
   
            this.loadByArgs(args);

    } 


    


    public loadAsync<T extends Asset>(bundleName:string,path:Paths,type:AssetType<T>):Promise<T>;
    public loadAsync<T extends Asset>(bundleName:string,path:Paths):Promise<T>;
    public loadAsync<T extends Asset>(path:Paths,type:AssetType<T>):Promise<T>;
    public loadAsync<T extends Asset>(path:Paths):Promise<T>

    public loadAsync<T extends Asset>(
        bundleName:string,
        path?:Paths | AssetType<T>|ProgressCallback|CompleteCallback,
        type?:AssetType<T>|ProgressCallback|CompleteCallback,
    ):Promise<T> {
        return new Promise((resolve, reject)=>{
            this.load(bundleName,path,type,(error:Error | null,asset:T)=>{
                if(error){
                    warn(error.message)
                }
                resolve(asset);
            })
        });
    }

 

    
    private parseLoadResArgs<T extends Asset>(
        paths: Paths,
        type?: AssetType<T> | ProgressCallback | CompleteCallback,
        onProgress?: AssetType<T> | ProgressCallback | CompleteCallback,
        onComplete?: ProgressCallback | CompleteCallback
    ) {
        let pathsOut: any = paths;
        let typeOut: any = type;
        let onProgressOut: any = onProgress;
        let onCompleteOut: any = onComplete;
        if (onComplete === undefined) {
            const isValidType = js.isChildClassOf(type as AssetType, Asset);
            if (onProgress) {
                onCompleteOut = onProgress as CompleteCallback;
                if (isValidType) {
                    onProgressOut = null;
                }
            }
            else if (onProgress === undefined && !isValidType) {
                onCompleteOut = type as CompleteCallback;
                onProgressOut = null;
                typeOut = null;
            }
            if (onProgress !== undefined && !isValidType) {
                onProgressOut = type as ProgressCallback;
                typeOut = null;
            }
        }
        return { paths: pathsOut, type: typeOut, onProgress: onProgressOut, onComplete: onCompleteOut };
    }
    
    private async loadByArgs<T extends Asset>(args: ILoadResArgs<T>) {
        if (args.bundle) {
            let bundle = assetManager.bundles.get(args.bundle);
            // 获取缓存中的资源包
            if (bundle) {
                this.loadByBundleAndArgs(bundle, args);
            }
            // 自动加载资源包
            else {
                const v = this.cdn ? this.bundles.get(args.bundle) : "";
                bundle = await this.loadBundle(args.bundle, v);
                if (bundle) this.loadByBundleAndArgs(bundle, args);
            }
        }
        // 默认资源包
        else {
            this.loadByBundleAndArgs(resources, args);
        }
    }
    private loadByBundleAndArgs<T extends Asset>(bundle: AssetManager.Bundle, args: ILoadResArgs<T>): void {
        if (args.dir) {
            if (args.preload) {
                bundle.preloadDir(args.paths as string, args.type, args.onProgress, args.onComplete);
            }
            else {
                bundle.loadDir(args.paths as string, args.type, args.onProgress, args.onComplete);
            }
        }
        else {
            if (args.preload) {
                bundle.preload(args.paths as any, args.type, args.onProgress, args.onComplete);
            }
            else {
                bundle.load(args.paths as any, args.type, args.onProgress, args.onComplete);
            }
        }
    }

    public loadBundle(url: string, v?: string) {
        return new Promise<AssetManager.Bundle>((resolve, reject) => {
            assetManager.loadBundle(url, { version: v }, (err, bundle: AssetManager.Bundle) => {
                if (err) {
                    return error(err);
                }
                resolve(bundle);
            });
        });
    }

    /**
     * 获取资源 
    */
    public get<T extends Asset>(path:string,type?:AssetType<T>,bundleName:string = this.defaultBundleName):T | null{
        let bundle:AssetManager.Bundle = assetManager.bundles.get(bundleName)!;
        return bundle.get(path,type);
    }

    public release(path:string,bundleName:string = this.defaultBundleName){
        const bundle  = assetManager.getBundle(bundleName);
        if (bundle) {
            const asset = bundle.get(path);
            if (asset) {
                this.releasePrefabDepsRecursively(asset)
            }
        }
    }

    private releasePrefabDepsRecursively(uuid:string| Asset){
        if (uuid instanceof Asset) {
            uuid.decRef();
        }else{
            const asset  = assetManager.assets.get(uuid);
            if (asset) {
                asset.decRef();
            }
        }
    }

    
}

export const resLoader = new ResLoader()